using System;
using System.Collections.Generic;
using System.Text;
using AppScan.Extensions;
using System.Threading;
using AppScan;
using AppScan.Configuration;
using System.IO;
using System.Windows.Forms;

/*
 * This product includes software developed by IBM Corporation (www.ibm.com).
 * All use and distribution of the IBM developed software is subject to Version 2.0
 * of the Apache License (http://www.apache.org/licenses/LICENSE-2.0).
 */

namespace PrivilegeEscalationRunnerExtension
{
	public class PrivilegeEscalationRunnerExtension : IExtensionLogic
	{
		#region Events & Delegates
		
		public delegate void LogMessageHandler(string logMsg);
		public delegate void StepCompleteHandler();
		public delegate void RunCompleteHandler();

		#endregion

		private PrivEscRunnerConfig _config = new PrivEscRunnerConfig();
		PrivilegeEscalationForm _mainForm = null;
		private string _extensionsDir;

		public PrivEscRunnerConfig Config
		{
			get { return _config; }
		}

		public string ExtensionsDir
		{
			get { return _extensionsDir; }
		}


		#region IExtensionLogic Members

		public ExtensionVersionInfo GetUpdateData(Edition edition, Version targetAppVersion)
		{
			return null;
		}

		public void Load(AppScan.IAppScan appScan, IAppScanGui appScanGui, string extensionDir)
		{
			_extensionsDir = extensionDir;
			appScanGui.ExtensionsMenu.Add(new MenuItem<EventArgs>("Privilege Escalation Runner", MenuItemCallback));
		}

		#endregion

		private void MenuItemCallback(EventArgs e)
		{
			Thread formThread = new Thread(new ThreadStart(ShowDialog));
			formThread.SetApartmentState(ApartmentState.STA);
			formThread.Start();

		}

		private void ShowDialog()
		{
			_mainForm = new PrivilegeEscalationForm(this);
			_mainForm.ShowDialog();
		}

		private void Log(string msg)
		{
			_mainForm.Invoke(_mainForm.logMsgHandler, new Object[] { msg });    // update progress bar
		}

		private void IncreaseProgress()
		{
			_mainForm.Invoke(_mainForm.stepCompleteHandler, null);    // update progress bar
		}

		public void StartRun()
		{
			IAppScan appScan = AppScanFactory.CreateInstance();

			Log("Running Scans");

			// Run a scan with no login
			string noAuthScan = RunSingleScan(null, appScan);
			// Run a scan with the additional login files
			List<string> otherScans = new List<string>();
			foreach (string loginFile in _config.OtherLoginFilesList)
			{
				string curScanFile = RunSingleScan(loginFile, appScan);
				otherScans.Add(curScanFile);
			}

			// Run the scan with the primary login
			string primeScanFileName = RunSingleScan(_config.PrimaryLoginFileName, appScan);

			// Configure the Privilege Escalation settings on the main scan file
			Log("Adding all roles to Privilege Escalation configuration");
			IPrivilegeEscalationRole nonAuthRole = PrivilegeEscalationRoleFactory.CreateInstance();
			nonAuthRole.RoleName = "NoAuth";
			nonAuthRole.IsNonAuthenticatedUser = true;
			nonAuthRole.PathToScanFile = noAuthScan;
			nonAuthRole.Comments = "Auto-created";
			appScan.Scan.ScanData.Config.PrivilegeEscalationRoles.Add(nonAuthRole);

			foreach (string curScanFile in otherScans)
			{
				FileInfo fi = new FileInfo(curScanFile);

				IPrivilegeEscalationRole curRole = PrivilegeEscalationRoleFactory.CreateInstance();
				curRole.RoleName = fi.Name;
				curRole.IsNonAuthenticatedUser = false;
				curRole.PathToScanFile = curScanFile;
				curRole.Comments = "Auto-created";

				appScan.Scan.ScanData.Config.PrivilegeEscalationRoles.Add(curRole);
			}

			// Run the test on the main scan
			Log("Running Privilege Escalation Testing");
			appScan.Scan.Scan(false, true);

			IncreaseProgress();

			// Save the scan to the results file
			Log("Saving Results Scan");
			appScan.Scan.SaveScanData(_config.ResultsScanFile);

			IncreaseProgress();

			_mainForm.Invoke(_mainForm.runCompleteHandler, null);    // update progress bar
		}

		private string RunSingleScan(string loginFileName, IAppScan appScan)
		{
			// Determine a display name for this phase
			string displayLoginFileName = null;
			if (loginFileName == null)
			{
				displayLoginFileName = "no login";
			}
			else
			{
				displayLoginFileName = "login file " + loginFileName;
			}
			Log("=====================================");
			Log("Current scan: " + displayLoginFileName);

			Log("Clearing Scan Data");
			// Clear the scan
			appScan.Scan.ResetScanData();

			Log("Loading Configuration");
			// Setup the configuration
			appScan.Scan.ScanData.Config.ImportFromTemplate(_config.ConfigScanTemplate);

			// Clear the test policy, as we only care about Priv Esc tests, which aren't a part of that list.
			Log("Clearing the Test Policy");
			appScan.Scan.ScanData.Config.TestPolicy.ClearVariants();

			Log("Setting URL Limit");
			// Set the URL limit
			appScan.Scan.ScanData.Config.LinkLimit.Enabled = true;
			appScan.Scan.ScanData.Config.LinkLimit.Value = _config.MaxUrlsPerScan;

			Log("Setting up login");
			// Setup the login
			if (loginFileName == null)
			{
				appScan.Scan.ScanData.Config.SessionManagement.Mode = SessionManagementMode.None;
			}
			else
			{
				appScan.Scan.ScanData.Config.SessionManagement.Mode = SessionManagementMode.Manual;
				StreamReader loginFileReader = new StreamReader(loginFileName);
				appScan.Scan.ScanData.Config.SessionManagement.Import(loginFileReader.BaseStream);
				loginFileReader.Close();
			}

			IncreaseProgress();

			Log("Running the explore");
			// Run the Explore
			ScanOperationResult scanResult = appScan.Scan.Scan(true, false);
			if (scanResult != ScanOperationResult.Succeeded)
			{
				MessageBox.Show("Explore with login file " + loginFileName + " failed with error " + scanResult.ToString());
				return null;
			}

			IncreaseProgress();

			// Save the scan
			string scanFileName = null;
			if (loginFileName == null)
			{
				FileInfo fi = new FileInfo(_config.PrimaryLoginFileName);
				scanFileName = _config.ScanFilesFolder + "\\" + fi.Name + ".no-login.scan";
			}
			else
			{
				FileInfo fi = new FileInfo(loginFileName);
				scanFileName = _config.ScanFilesFolder + "\\" + fi.Name + ".scan";
			}
			Log("Saving the scan to file " + scanFileName);
			appScan.Scan.SaveScanData(scanFileName);

			IncreaseProgress();

			Thread.Sleep(1000);

			return scanFileName;
		}
	}
}
